#include <math.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

typedef struct{
    unsigned int a0:1;
    unsigned int a1:1;
    unsigned int a2:1;
    unsigned int a3:1;
    unsigned int a4:1;
    unsigned int a5:1;
    unsigned int a6:1;
    unsigned int a7:1;
}bits;
//struct bits myBits[2];
typedef struct{
	bits byteSalida[1];
	int puntero;
}grabar;
static grabar byteGrabar;

void setBits(int lugar, int valor, bits *miByte){
	int arrayPos=lugar/8;
	int lugarPos=lugar % 8;
	switch(lugarPos){
		case 0:
				miByte[arrayPos].a0=valor;
				break;
		case 1:
				miByte[arrayPos].a1=valor;
				break;
		case 2:
				miByte[arrayPos].a2=valor;
				break;
		case 3:
				miByte[arrayPos].a3=valor;
				break;
		case 4:
				miByte[arrayPos].a4=valor;
				break;
		case 5:
				miByte[arrayPos].a5=valor;
				break;
		case 6:
				miByte[arrayPos].a6=valor;
				break;
		case 7:
				miByte[arrayPos].a7=valor;
				break;
	}
	
}

void graboBit(int calculoBits,bits *miByte, FILE *salida){
	//printf("Cantidad de bits: %d\n",calculoBits);
	int i=0;
	for(i=0;i<calculoBits;i++){
		switch(byteGrabar.puntero){
			case 0:
					byteGrabar.byteSalida->a0=getBit(i,miByte);
					break;
			case 1:
					byteGrabar.byteSalida->a1=getBit(i,miByte);
					break;
			case 2:
					byteGrabar.byteSalida->a2=getBit(i,miByte);
					break;
			case 3:
					byteGrabar.byteSalida->a3=getBit(i,miByte);
					break;
			case 4:
					byteGrabar.byteSalida->a4=getBit(i,miByte);
					break;
			case 5:
					byteGrabar.byteSalida->a5=getBit(i,miByte);
					break;
			case 6:
					byteGrabar.byteSalida->a6=getBit(i,miByte);
					break;
			case 7:
					byteGrabar.byteSalida->a7=getBit(i,miByte);
					break;
		}
		byteGrabar.puntero++;
		if (byteGrabar.puntero == 8){
		//Imprime salida
			//printf("Contenido:%d:%d:%d:%d:%d:%d:%d:%d:\n",byteGrabar.byteSalida->a0,byteGrabar.byteSalida->a1,byteGrabar.byteSalida->a2,byteGrabar.byteSalida->a3,byteGrabar.byteSalida->a4,byteGrabar.byteSalida->a5,byteGrabar.byteSalida->a6,byteGrabar.byteSalida->a7);
			fwrite(byteGrabar.byteSalida,1,1,salida);
			fflush(salida);
			byteGrabar.puntero=0;
		}
		
	}
}
void grabarResto(FILE *salida){
	int i=0;
	for(i=byteGrabar.puntero;i<8;i++){
		//getBit(i,miByte);
		switch(byteGrabar.puntero){
			case 0:
					byteGrabar.byteSalida->a0=0;
					break;
			case 1:
					byteGrabar.byteSalida->a1=0;
					break;
			case 2:
					byteGrabar.byteSalida->a2=0;
					break;
			case 3:
					byteGrabar.byteSalida->a3=0;
					break;
			case 4:
					byteGrabar.byteSalida->a4=0;
					break;
			case 5:
					byteGrabar.byteSalida->a5=0;
					break;
			case 6:
					byteGrabar.byteSalida->a6=0;
					break;
			case 7:
					byteGrabar.byteSalida->a7=0;
					break;
		}
	}
	//Imprime salida
	//printf("Contenido:%d:%d:%d:%d:%d:%d:%d:%d:\n",byteGrabar.byteSalida->a0,byteGrabar.byteSalida->a1,byteGrabar.byteSalida->a2,byteGrabar.byteSalida->a3,byteGrabar.byteSalida->a4,byteGrabar.byteSalida->a5,byteGrabar.byteSalida->a6,byteGrabar.byteSalida->a7);
	fwrite(byteGrabar.byteSalida,1,1,salida);
	fflush(salida);
	byteGrabar.puntero=0;
		
}
int getBit(int lugar, bits *miByte){
	int arrayPos=lugar/8;
	int lugarPos=lugar % 8;
	switch(lugarPos){
		case 0:
				return miByte[arrayPos].a0;
				break;
		case 1:
				return miByte[arrayPos].a1;
				break;
		case 2:
				return miByte[arrayPos].a2;
				break;
		case 3:
				return miByte[arrayPos].a3;
				break;
		case 4:
				return miByte[arrayPos].a4;
				break;
		case 5:
				return miByte[arrayPos].a5;
				break;
		case 6:
				return miByte[arrayPos].a6;
				break;
		case 7:
				return miByte[arrayPos].a7;
				break;
	}
	
}

void convertirABits(int k, long int binario, long int unario, FILE *salida){
//Como se cuantos bits ocupa?
//R: k + unario+1
	int calculoBits=k+unario+1;
	int cantidadBytes=ceil(calculoBits/8);
	bits miByte[cantidadBytes];
	int i = 0;
	memset(miByte,0,cantidadBytes); 
	
	if (k==0){
		setBits(0,0,miByte);
	}
	int recorre=k-1;
	while (binario > 0){
		if (binario%2==1){
			setBits(recorre,1,miByte);
		}
		recorre--;
		binario = binario/2 ;
	}
	recorre=k;
	for(i=0;i<unario;i++){
		setBits(recorre,0,miByte);
		recorre++;
	}
	setBits(recorre,1,miByte);

	graboBit(calculoBits,miByte,salida);

}

void escriboRunMode(int run_mode, FILE *salida){
	int calculoBits=run_mode+1;
	int cantidadBytes=ceil(calculoBits/8);
	bits miByte[cantidadBytes];
	int i = 0;
	memset(miByte,0,cantidadBytes); 
	int recorre=0;
	for(i=0;i<run_mode;i++){
		setBits(recorre,0,miByte);
		recorre++;
	}
	setBits(recorre,1,miByte);

	graboBit(calculoBits,miByte,salida);

}

void inttobit(long int a, char bin[32]){
	
	int i = 0;
	for(i = 0; i<32; i++){
		bin[i]= '0';
	}
	
	int cnt = 31;
	while (a > 0){
		if (a%2==1){
		   bin[cnt] = '1';
		}
		cnt--;
		a = a/2 ;
	}
}
long int hextoint(char c){
	char b[10];
	int j = 0;
	for(j = 0; j < 10; j++){
		b[j] = '0';
	}
	sprintf(b,"%s%x", "0x", c);
	int unsigned long i = (int unsigned long)strtoul (b, NULL, 0);
	return i;
}
/*
 * devuelve el contexto de un pixel (dado por la posicion)
 * contexto de la siguiente forma
 * contexto[0][0] corresponde a RED   de "a"
 * contexto[0][1] corresponde a GREEN de "a"
 * contexto[0][2] corresponde a BLUE  de "a"
 * contexto[1][0] corresponde a RED   de "b"
 * .........................
 * contexto[3][2] corresponde a BLUE  de "d"
 * si es en escala de grises solo utiliza el indice correspondente a RED
 */
//char ** obtenerContexto(int ancho, int alto, int bytes_por_pixel, int posicion, char *lineaAnterior, char *lineaActual/*, char contexto[4][3]*/){
void obtenerContexto(int ancho, int alto, int bytes_por_pixel, int posicion, char *lineaAnterior, char *lineaActual, long int contexto[4][3]){
	int i = 0;
	for(i = 0; i<4; i++){
		contexto[i][0] = 0;
		contexto[i][1] = 0;
		contexto[i][2] = 0;
	}
	
	//if((posicion % bytes_por_pixel) == 0){
		// calculo "a"
		if(posicion == 0){ // esta en el borde izquierdo "a" = 0
			contexto[0][0] = 0; // R
			contexto[0][1] = 0; // G
			contexto[0][2] = 0; // B
		}
		else{
			contexto[0][0] = hextoint(lineaActual[((posicion-1)* bytes_por_pixel)]);//lineaActual[posicion - bytes_por_pixel];
			if(bytes_por_pixel == 3){ // tengo que obtener G y B
				contexto[0][1] = hextoint(lineaActual[((posicion-1)* bytes_por_pixel)+1]); // G
				contexto[0][2] = hextoint(lineaActual[((posicion-1)* bytes_por_pixel)+2]); // B
			}
		}
		// calculo "b"
		contexto[1][0] = hextoint(lineaAnterior[(posicion* bytes_por_pixel)]);
		if(bytes_por_pixel == 3){ // tengo que obtener G y B
			contexto[1][1] = hextoint(lineaAnterior[(posicion* bytes_por_pixel)+ 1]); // G
			contexto[1][2] = hextoint(lineaAnterior[(posicion* bytes_por_pixel)+ 2]); // B
		}
		// calculo "c"
		if(posicion == 0){ // esta en el borde izquierdo "c" = 0
			contexto[2][0] = 0; // R
			contexto[2][1] = 0; // G
			contexto[2][2] = 0; // B
		}
		else{
			contexto[2][0] = hextoint(lineaAnterior[((posicion-1)*bytes_por_pixel)]);
			if(bytes_por_pixel == 3){ // tengo que obtener G y B
				contexto[0][1] = hextoint(lineaAnterior[((posicion-1)*bytes_por_pixel)+1]); // G
				contexto[0][2] = hextoint(lineaAnterior[((posicion-1)*bytes_por_pixel)+2]); // B
			}
		}
		// calculo "d"
		if(posicion == ancho -1){ // esta en el bode derecho "d" = 0
			contexto[3][0] = 0; // R
			contexto[3][1] = 0; // G
			contexto[3][2] = 0; // B
		}
		else{
			contexto[3][0] = hextoint(lineaAnterior[((posicion+1)* bytes_por_pixel)]);
			if(bytes_por_pixel == 3){ // tengo que obtener G y B
				contexto[3][1] = hextoint(lineaAnterior[((posicion+1)* bytes_por_pixel)+1]); // G
				contexto[3][2] = hextoint(lineaAnterior[((posicion+1)* bytes_por_pixel)+2]); // B
			}
		}
	//}
//	printf("POSICIONES:%d-%d-%d-%d-%d\n",posicion, ((posicion-1)* bytes_por_pixel)+indiceColor,(posicion* bytes_por_pixel)+indiceColor,((posicion-1)*bytes_por_pixel)+indiceColor,((posicion+1)* bytes_por_pixel)+indiceColor);
//	printf("CONTEXTO:%d-%d-%d-%d\n",contexto[0][0],contexto[1][0],contexto[2][0],contexto[3][0]);
	
	//return contexto;
	
}
/* devuelve el gradiente dado el contexto
 * en caso que sea imagen color con la parte "A" del problema donde se comprimen los planos de color por separado:
 * gradiente[0][0] corresponde a g1 del plano R
 * gradiente[0][1] corresponde a g2 del plano R
 * gradiente[0][2] corresponde a g3 del plano R
 * ............ 
 * gradiente[2][0] corresponde a g1 del plano B 
 * gradiente[2][0] corresponde a g1 del plano B
 * en caso de que sea imagen gris o con la parte "B" del problema solo se utiliza gradientes[0]:
 * gradiente[0][0] corresponde a g1
 * gradiente[0][1] corresponde a g2
 * gradiente[0][2] corresponde a g3 
 */
void calcularGradiente(long int contexto[4][3], long int gradientes[3][3], char parametro_CODIFICACION, int tipoArchivo){
	int i = 0;
	for(i = 0; i < 3; i++){
		gradientes[i][0] = 0;
		gradientes[i][1] = 0;
		gradientes[i][2] = 0;
	}
	if((strncmp("A",&parametro_CODIFICACION,1)==0) || tipoArchivo == 5){ // se comprimen los planos por separado
		// g1 = d-b
		gradientes[0][0] = contexto[3][0] - contexto[1][0]; // R
		gradientes[0][1] = contexto[3][1] - contexto[1][1]; // G
		gradientes[0][2] = contexto[3][2] - contexto[1][2]; // B
		// g2 = b-c
		gradientes[1][0] = contexto[1][0] - contexto[2][0]; // R
		gradientes[1][1] = contexto[1][1] - contexto[2][1]; // G
		gradientes[1][2] = contexto[1][2] - contexto[2][2]; // B
		// g3 = c-a
		gradientes[2][0] = contexto[2][0] - contexto[0][0]; // R
		gradientes[2][1] = contexto[2][1] - contexto[0][1]; // G
		gradientes[2][2] = contexto[2][2] - contexto[0][2]; // B
	}
	else{
		// efectuo la transformacion
		// R' = (R - G) mod 256 para a,b,c,d
/*		contexto[0][0] = (contexto[0][0] - contexto[0][1]) % 256;
		contexto[1][0] = (contexto[1][0] - contexto[1][1]) % 256;
		contexto[2][0] = (contexto[2][0] - contexto[2][1]) % 256;
		contexto[3][0] = (contexto[3][0] - contexto[3][1]) % 256;*/
		contexto[0][0] =(unsigned char) ((contexto[0][0] - contexto[0][1])%256);
		contexto[1][0] =(unsigned char) ((contexto[1][0] - contexto[1][1])%256);
		contexto[2][0] =(unsigned char) ((contexto[2][0] - contexto[2][1])%256);
		contexto[3][0] =(unsigned char) ((contexto[3][0] - contexto[3][1])%256);
		// G' = G para a,b,c,d no hago nada
		// B' = (B - G) mod 256 para a,b,c,d
/*		contexto[0][2] = (contexto[0][2] - contexto[0][1]) % 256;
		contexto[1][2] = (contexto[1][2] - contexto[1][1]) % 256;
		contexto[2][2] = (contexto[2][2] - contexto[2][1]) % 256;
		contexto[3][2] = (contexto[3][2] - contexto[3][1]) % 256;*/
		contexto[0][2] =(unsigned char) ((contexto[0][2] - contexto[0][1])%256);
		contexto[1][2] =(unsigned char) ((contexto[1][2] - contexto[1][1])%256);
		contexto[2][2] =(unsigned char) ((contexto[2][2] - contexto[2][1])%256);
		contexto[3][2] =(unsigned char) ((contexto[3][2] - contexto[3][1])%256);
		
		// g1 = d - b
		gradientes[0][0] = contexto[3][0] - contexto[1][0]; // R
		gradientes[0][1] = contexto[3][1] - contexto[1][1]; // G
		gradientes[0][2] = contexto[3][2] - contexto[1][2]; // B
		// g2 = b-c
		gradientes[1][0] = contexto[1][0] - contexto[2][0]; // R
		gradientes[1][1] = contexto[1][1] - contexto[2][1]; // G
		gradientes[1][2] = contexto[1][2] - contexto[2][2]; // B
		// g3 = c-a
		gradientes[2][0] = contexto[2][0] - contexto[0][0]; // R
		gradientes[2][1] = contexto[2][1] - contexto[0][1]; // G
		gradientes[2][2] = contexto[2][2] - contexto[0][2]; // B
	}
}
void cuantizar(long int gradientes[3][3]){
	int i = 0;
	int j = 0;
	for(i = 0; i < 3; i++){
		for(j = 0; j < 3; j++){
			if((gradientes[i][j] > 0) && (gradientes[i][j] <= 3)){
				gradientes[i][j] = 1;
			}
			if((gradientes[i][j] > 3) && (gradientes[i][j] <= 7)){
				gradientes[i][j] = 2;
			}
			if((gradientes[i][j] > 7) && (gradientes[i][j] <= 21)){
				gradientes[i][j] = 3;
			}
			if((gradientes[i][j] > 21)){
				gradientes[i][j] = 4;
			}
			
			if((gradientes[i][j] < 0) && (gradientes[i][j] >= -3)){
				gradientes[i][j] = -1;
			}
			if((gradientes[i][j] < -3) && (gradientes[i][j] >= -7)){
				gradientes[i][j] = -2;
			}
			if((gradientes[i][j] < -7) && (gradientes[i][j] >= -21)){
				gradientes[i][j] = -3;
			}
			if((gradientes[i][j] < -21)){
				gradientes[i][j] = -4;
			}
		}
	}
	// si el primer elemento es negativo, le cambio el signo a todo el gradiente
	for(i = 0; i < 3; i++){
		if(gradientes[i][0] < 0){
			for(j = 0; j < 3; j++){
				gradientes[i][j] = -gradientes[i][j];
			}
		}
	}
	
}

/* Predictor dado el contexto a, b, c
 * calcula para cada color RGB el predictor
 * predictor[0] = predictor de R
 * predictor[1] = predictor de G 
 * predictor[2] = predictor de B 
 */
void predecirX(long int contexto[4][3], long int predictor[3]){
	predictor[0] = 0;
	predictor[1] = 0;
	predictor[2] = 0;
	/*
	 * PARA R i = 0
	 * PARA G i = 1
	 * PARA B i = 2
	 */
	int i = 0;
	for(i = 0; i< 3; i++){
		// min(a,b) if(c >= max(a,b))
		if(contexto[2][i] >= MAX(contexto[0][i], contexto[1][i])){
			predictor[i] = MIN(contexto[0][i], contexto[1][i]);
		}
		else{
			// max(a,b) if(c <= min(a,b))
			if(contexto[2][i] <= MIN(contexto[0][i], contexto[1][i])){
				predictor[i] = MAX(contexto[0][i], contexto[1][i]);
			}
			// otherwise a+b-c
			else{
				predictor[i] = contexto[0][i] + contexto[1][i] - contexto[2][i];
			}
		}
	}
}
void correccionX(long int Xreal[3], long int predictor[3], long int B[3], long int C[3], long int N, int bytes_por_pixel){
	long int e[3] = {0,0,0};
	int i =  0;
	for(i = 0; i < bytes_por_pixel; i++){
		e[i] = Xreal[i] - predictor[i];
		B[i] = B[i] + e[i];
		
		if(B[i] <= -N){
			C[i] =  C[i] - 1;
			B[i] = B[i] + N;
			if(B[i] <= -N){
				B[i] = -N + 1;
			}
		}
		else{
			if(B[i] > 0){
				C[i] =  C[i] + 1;
				B[i] = B[i] - N;
				if(B[i] > 0){
					B[i] = 0;
				}
			}
		}
	}
	i=0;
	for(i = 0; i < 3; i++){
		predictor[i] = predictor[i] + C[i];
	}
}

void remapError(long int error[3]){
	int i = 0;
	for(i = 0; i < 3; i++){
		if(error[i] < 0){
			error[i] = -2 * error[i] + 1;
		}
		else{
			error[i] = 2 * error[i];
		}
	}
}
long int remapErrorDeAUno(long int error){
	if(error < 0){
		return (-2 * error + 1);
	}
	else{
		return (2 * error);
	}
}
int binarytoint(char a[32]){
	int j = 0;
	for(j = 0; j<32; j++){
		printf("%c",a[j]);
	}
	size_t len = strlen(a);
	int i = 0;
	int dec = 0;
	//while(i < len){
	for(i = 0; i < len; i++){
		dec = (dec << 1) + (a[i] - '0');
	}

	return dec;
}
void codificarGolomb(long int error[3], char golomb[3][32], int k[3], int bytes_por_pixel, FILE *salida){
	int i = 0;
	int j = 0;
	for(i = 0; i<3; i++){
		for(j = 0; j<32; j++){
			golomb[i][j]= '0';

		}
	}
	char tmp[32];
	for(i = 0; i < 32; i++){
		tmp[i] = '0';
	}
	int binario = 0;
	int unario = 0;

	i=0;
	for(i = 0; i < bytes_por_pixel; i++){
		binario = error[i] % (int)pow(2, k[i]);
		unario = floor(error[i] / pow (2.0, k[i]));
		//printf("k = %d, error = %d <--> binario = %d, unario = %d\n", k[i], error[i], binario, unario);
		convertirABits(k[i],binario,unario,salida);
	}
}
int main(int argc, char **argv)
{
	clock_t comienzo,final;
	comienzo=clock();
	
	FILE *EImagen, *SImagen;
	char tipo[2];
	char recorre[1];
	int leidos;
	int ancho=0;
	int alto=0;
	int color=0;
	int bits_a_leer=0;
	int bytes_por_pixel=1;
	int tipoArchivo=5;//Solo puede ser 5 o 6
	int parametro_N;
	char parametro_CODIFICACION='0';
	char SEPARADOR=0x0a;
	
	byteGrabar.puntero=0;
	if (argc != 4 && argc !=5){
		perror("Parametros incorrecto. Parametros adecuados\n<Nombre archivo entrada> <Nombre archivo salida> <N> [A|B]\n");
		return -1;
	}
	parametro_N=atoi(argv[3]);
	if (argc ==5){
		parametro_CODIFICACION=argv[4][0];
	}
	printf("Parametros ingresados %s %s %d %c\n", argv[1], argv[2], parametro_N, parametro_CODIFICACION);
	//return 0;
	EImagen=fopen(argv[1], "rb"); 
	if (EImagen==NULL){
		perror("No se puede abrir archivo");
		return -1;
	}
	//char *nombre_salida=strcat(argv[1], ".tmp1");
	SImagen=fopen(argv[2], "w"); 
	if (SImagen==NULL){
		perror("No se puede crear archivo");
		return -1;
	}

	fprintf(SImagen,"%d",parametro_N);
	if (parametro_N > 256 || parametro_N < 1){
		perror("Parametro N incorrecto, rango permitido 1 a 256\n");
		return -1;
	}
	fprintf(SImagen,"%c",SEPARADOR);
	fprintf(SImagen,"%c",parametro_CODIFICACION);

	leidos = fread(tipo, 1, 2, EImagen);
	if (tipo[0]!='P'){
		perror("Formato incorrecto. Archivo no comienza con P\n");
		return -1;
	}
	if (tipo[1]!='5' && tipo[1]!='6'){
		perror("Formato incorrecto. Archivo no es P5 ni P6\n");
		return -1;
	}
	tipoArchivo=tipo[1]-48;//5 o 6
	if(tipoArchivo==6){
		if (!strncmp("0",&parametro_CODIFICACION,1)){
			perror("Se ingreso un archivo color pero no se indico el tipo de codificacion\n");
			return -1;
		}
		if ((strncmp("A",&parametro_CODIFICACION,1)!=0) && (strncmp("B",&parametro_CODIFICACION,1)!=0)){
			perror("Para imagenes color el tipo de codificacion permitido es A o B (solo mayusculas)\n");
			return -1;
		}
	}
	if(tipoArchivo==5 && (strncmp("0",&parametro_CODIFICACION,1))){
		printf("Se ingreso un archivo blanco negro, se ignora tipo de codificacion ingresado\n");
	}
	//Debo leer un separador.
	leidos = fread(recorre, 1, 1, EImagen);
	fprintf(SImagen,"%c",tipo[0]);
	fprintf(SImagen,"%c",tipo[1]);

	if (recorre[0]!=10){
		perror("Formato incorrecto. Archivo sin separador esperado\n");
		return -1;
	}
	fprintf(SImagen,"%c",SEPARADOR);
	//Veo si lo que viene es un mensaje, o el ancho
	leidos = fread(recorre, 1, 1, EImagen);
	if (recorre[0]==35){//Linea de comentarios
		//Debo ignorara todo hasta que encuentre el separador 0A
		while (recorre[0]!=10){
			fprintf(SImagen,"%c",recorre[0]);
			leidos = fread(recorre, 1, 1, EImagen);
		}
		fprintf(SImagen,"%c",SEPARADOR);
		leidos = fread(recorre, 1, 1, EImagen);
	}
	//O Pase el mensaje, y tengo en recorre el primer digito del ancho
	while (recorre[0]!=32){
		ancho = ancho*10+recorre[0]-48;
		leidos = fread(recorre, 1, 1, EImagen);
	}
	fprintf(SImagen,"%d",ancho);
	fprintf(SImagen," ");
	printf("Ancho %d\n",ancho);
	//Leo el siguiente al espacio
	leidos = fread(recorre, 1, 1, EImagen);
	while (recorre[0]!=10){
		alto = alto*10+recorre[0]-48;
		leidos = fread(recorre, 1, 1, EImagen);
	}
	fprintf(SImagen,"%d",alto);
	fprintf(SImagen,"%c",SEPARADOR);
	printf("Alto %d\n",alto);
	//Leo el siguiente al separador
	leidos = fread(recorre, 1, 1, EImagen);
	while (recorre[0]!=10){
		color = color*10+recorre[0]-48;
		leidos = fread(recorre, 1, 1, EImagen);
	}
	fprintf(SImagen,"%d",color);
	fprintf(SImagen,"%c",SEPARADOR);
	printf("Color ASCII %d\n",color);
	bits_a_leer=1;
	if (color > 255){
		bits_a_leer=2;
	}
	bytes_por_pixel=1;
	//if (color == 255){
	if (tipoArchivo==6){
		bytes_por_pixel=3;
	}
	//Tengo leido el 0A, a partir de aca, arranca la imagen de verdad....

	// Para recorrer el archivo	
//	long int punteroFile = ftell(EImagen);
	
//	int indiceColor=0;
//	for(indiceColor; indiceColor<bytes_por_pixel; indiceColor++){
//	fseek(EImagen, punteroFile, SEEK_SET);

	char lineaActual[ancho*bytes_por_pixel];
	char lineaAnterior[ancho*bytes_por_pixel];

// Para recorrer el archivo	
//	while(!feof(EImagen)){ /* feof comprueba si llegas al final del fichero */

	int ini = 0;
	for(ini = 0; ini < ancho*bytes_por_pixel; ini++){
		lineaActual[ini] = '\0';
		lineaAnterior[ini] = '\0';
	}
	long int contexto[4][3] = {{0,0,0},{0,0,0},{0,0,0},{0,0,0}};
	long int gradientes[3][3] = {{0,0,0}, {0,0,0}, {0,0,0}};
	long int Xpred[3] = {0,0,0};
	long int Xreal[3] = {0,0,0};
	long int error[3] = {0,0,0};// error[0] = error R; error[1] = error G; error[2] = error B
	long int error_tmp[3] = {0,0,0};

	// variables usadas para la funcion correccionX
	long int Bcorreccion[3] = {0,0,0};
	long int Ccorreccion[3] = {0,0,0};
	long int Ncorreccion = 0;

	int A[3] = {8,8,8};
	int k[3] = {0,0,0};
	double n = 1;
	char golomb[3][32];
	int run_mode=0;
	
	//int nroColumna = 0;
	
	int nroFila = 0;
	for(nroFila; nroFila<alto; nroFila++){
		leidos = fread(lineaActual, bytes_por_pixel, ancho, EImagen);
		int nroColumna = 0;
		for(nroColumna; nroColumna<ancho; nroColumna++){
			//printf("\tNRO FILA %d ## NRO COLUMNA ==> %d\n", nroFila, nroColumna);
			if(bytes_por_pixel == 3){
				Xreal[0] = hextoint(lineaActual[(nroColumna*bytes_por_pixel)]);
				Xreal[1] =  hextoint(lineaActual[(nroColumna*bytes_por_pixel) + 1]);
				Xreal[2] =  hextoint(lineaActual[(nroColumna*bytes_por_pixel) + 2]);
				if(strncmp("B",&parametro_CODIFICACION,1)==0){
					/*Xreal[0] = (Xreal[0]-Xreal[1])%256;
					Xreal[2] = (Xreal[2]-Xreal[1])%256;*/
					Xreal[0] = (unsigned char)((Xreal[0]-Xreal[1])%256);
					Xreal[2] = (unsigned char)((Xreal[2]-Xreal[1])%256);
				}
			}
			else{
			//Es 1
				Xreal[0] = hextoint(lineaActual[(nroColumna*bytes_por_pixel)]);
			}
			obtenerContexto(ancho, alto, bytes_por_pixel, nroColumna, lineaAnterior, lineaActual, contexto);
			calcularGradiente(contexto, gradientes, parametro_CODIFICACION, tipoArchivo);

			cuantizar(gradientes);
			predecirX(contexto,Xpred);
			//printf("VER %d, %d, %d, %d, %d, %d, %d, %d - %d %d\n",Xreal[0],contexto[0][0],contexto[1][0],contexto[2][0],contexto[3][0],gradientes[0][0],gradientes[1][0],gradientes[2][0],nroFila, nroColumna);

			if (tipoArchivo==5 && gradientes[0][0]==0 && gradientes[1][0]==0 && gradientes[2][0]==0 && contexto[0][0]== Xreal[0]){
			///Entro e n run mode
				if(run_mode==0){
					//printf("\t ENTRO RUN MODE NRO FILA %d ## NRO COLUMNA ==> %d\n", nroFila, nroColumna);

					ini=0;
					for(ini = 0; ini < bytes_por_pixel; ini ++){
						error[ini] = Xreal[ini] - Xpred[ini];
						//printf("VERDAD %d, %d, %d\n",Xreal[ini],error[ini],Xpred[ini]);
					}
					//remapError(error);
					ini =0;
					for(ini = 0; ini < bytes_por_pixel; ini++){
						//printf("A[%d]=%d ## n = %f ## parametro_N %d ## ",ini, A[ini], n, parametro_N);
						if(A[ini] != 0){
							float a = log2((float)A[ini]) - log2((float)n); // log(a/b) = log(a) - log(b)
							k[ini] = (int)ceil(a);//(int)ceil(log2(A[ini]/n));
							k[ini] = abs(k[ini]);
							error_tmp[ini] = remapErrorDeAUno(error[ini]);
						}
						else{
							k[ini] = 1;
							//error_tmp[ini] = remapErrorDeAUno(error[ini]);
							error_tmp[ini] = remapErrorDeAUno(-error[ini] - 1);
						}
						//printf("k[0]=%d\n",k[0]);
						if(n == parametro_N){
							A[ini] = (int)floor((double)(A[ini]/2.0));
						}
						A[ini] = A[ini] + abs(error[ini]);
					}
					if(n == parametro_N){
						n = (double)((double)n/2.0);
					}
					n++;

					codificarGolomb(error_tmp, golomb, k, bytes_por_pixel, SImagen);
				}

				run_mode++;
				//if((nroColumna+1)==ancho){
				if((nroColumna+1)==ancho){
					//printf("Run mode %d %d\n", (run_mode-1), nroColumna);
					escriboRunMode((run_mode-1),SImagen);

					/*printf("Run mode %d\n", run_mode);
					escriboRunMode(run_mode,SImagen);*/
					run_mode=0;
				}
			}
			else{
				if (run_mode>0){
					//printf("\t SALGO RUN MODE NRO FILA %d ## NRO COLUMNA ==> %d\n", nroFila, nroColumna);
					escriboRunMode((run_mode-1),SImagen);
					//printf("Run mode %d %d\n", (run_mode-1), nroColumna);

/*					escriboRunMode(run_mode,SImagen);
					printf("Run mode %d\n", run_mode);*/
					run_mode=0;
				///Salgo del run mode
				///Escribo el run mode
				}
/*				cuantizar(gradientes);
				predecirX(contexto,Xpred);*/

				// calculo el error
				ini=0;
				for(ini = 0; ini < bytes_por_pixel; ini ++){
					error[ini] = Xreal[ini] - Xpred[ini];
					//printf("VERDAD %d, %d, %d\n",Xreal[ini],error[ini],Xpred[ini]);
				}
				//remapError(error);
				ini =0;
				for(ini = 0; ini < bytes_por_pixel; ini++){
					//printf("A[%d]=%d ## n = %f ## parametro_N %d ## ",ini, A[ini], n, parametro_N);
					if(A[ini] != 0){
						float a = log2((float)A[ini]) - log2((float)n); // log(a/b) = log(a) - log(b)
						k[ini] = (int)ceil(a);//(int)ceil(log2(A[ini]/n));
						k[ini] = abs(k[ini]);
						error_tmp[ini] = remapErrorDeAUno(error[ini]);
					}
					else{
						k[ini] = 1;
						//error_tmp[ini] = remapErrorDeAUno(error[ini]);
						error_tmp[ini] = remapErrorDeAUno(-error[ini] - 1);
					}
					//printf("k[0]=%d\n",k[0]);
					if(n == parametro_N){
						A[ini] = (int)floor((double)(A[ini]/2.0));
					}
					A[ini] = A[ini] + abs(error[ini]);
				}
				if(n == parametro_N){
					n = (double)((double)n/2.0);
				}
				n++;
				//remapError(error);
				codificarGolomb(error_tmp, golomb, k, bytes_por_pixel, SImagen);
			}
		}
		//strcpy(lineaAnterior, "");
		//strcpy(lineaAnterior, lineaActual);
		memcpy(lineaAnterior, lineaActual,ancho*bytes_por_pixel);
	}
	if(byteGrabar.puntero != 0){
	//Me quedan unos bits sin grabar
		grabarResto(SImagen);
	}
	fclose(EImagen);
	fclose(SImagen);
	printf("comienzo %ld\n",comienzo);
	final=clock();
	printf("fin %ld\n",final);
	double secs;
	secs = (double)(final - comienzo) / CLOCKS_PER_SEC;
	printf("%.16g milisegundos\n", secs * 1000.0);
	
	return 0;
}